/* Name: GameScreen
 * Author: John Gideon
 * Date: 5-4-12
 * Description: Draws the in game screen
 * Revisions: See Subversion logs at http://code.google.com/p/software-engineering-2012/source/list
 */

import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.util.Iterator;
import java.util.Set;

import org.jgrapht.alg.ConnectivityInspector;
import org.jgrapht.alg.KruskalMinimumSpanningTree;
import org.jgrapht.graph.DefaultWeightedEdge;
import org.jgrapht.graph.SimpleWeightedGraph;

public class GameScreen extends Item {
	private static int width;
	private static int height;
	private static Planet selectedPlanet = null;
	private static Planet earth = null;
	private static CreateWormholeBtn createWormholeBtn;
	private static CreateProbeBtn createProbeBtn;
	private static PauseBtn pauseBtn;
	private static CloseBtn closeBtn;
	private static MuteBtn muteBtn;
	private long randomEventRefTime = 0;
	private long randomEventCurrentTime = 0;
	private long secondRefTime = 0;
	private long secondCurrentTime = 0;
	public static Boolean secondPassed = false;
	public static float totalOre = 0;
	public static float totalFuel = 0;
	private static SimpleWeightedGraph<Planet, DefaultWeightedEdge> winPlanetGraph = null;
	private static KruskalMinimumSpanningTree<Planet, DefaultWeightedEdge> minTree = null;
	public static Boolean gameComplete = false;
	private static Boolean roomCreationErrorFlag = false;
	public static Boolean gameFailed = false;
	private static int frameCount = 0;
	private static int spareTime = 0;
	
	// Creates a new game screen
	public GameScreen() {
		Planet.numConquered = 0;
		Planet.numPlanets = 0;
		gameComplete = false;
		gameFailed = false;
		Game.create((Item)this, 999999); // Needed for update, draw, and destroy
		colType = COL_NONE; // Needed for collision checking	
		do {
			roomCreationErrorFlag = false;
			int numPlanet = 0;
			int numAntimatter = 0;
			int numAsteroidField = 0;
			int numNebula = 0;
			switch (Game.roomSize) { // Sets up based on user selected game size
				case Game.SIZE_SMALL:
					width = (int) (1440*2.5);
					height = (int) (900*2.5);
					numPlanet = 20;
					numAntimatter = 10;
					numAsteroidField = 5;
					numNebula = 5;
					break;
				case Game.SIZE_MEDIUM:
					width = (int) (1440*3.25);
					height = (int) (900*3.25);
					numPlanet = 35;
					numAntimatter = 15;
					numAsteroidField = 7;
					numNebula = 7;
					break;
				case Game.SIZE_LARGE:
					width = 1440*4;
					height = 900*4;
					numPlanet = 50;
					numAntimatter = 20;
					numAsteroidField = 10;
					numNebula = 10;
					break;
			}
			switch (Game.difficulty) { // Sets up based on difficulty
				case Game.DIFFICULTY_EASY:
					spareTime = 2;
					break;
				case Game.DIFFICULTY_MEDIUM:
					numAntimatter += 4;
					numAsteroidField -= 2;
					numNebula -= 1;
					spareTime = 1;
					break;
				case Game.DIFFICULTY_HARD:
					numAntimatter += 8;
					numAsteroidField -= 3;
					numNebula -= 2;
					spareTime = 1;
					break;
			}
			int totItems = numPlanet + numAntimatter + numAsteroidField + numNebula;
	
			Planet.resetNames();
			new Fog();
			new ViewControl();
	
			// Create initial Earth area
			earth = new Planet(0);
			earth.active = true;
			Fog.uncover(earth.x, earth.y, 500);
			ViewControl.centerOn(earth.x, earth.y);
			float dis = Game.randomInt(400, 550);
			float dir = Game.randomFloat((float) (2*Math.PI));
			int tmpX = (int) (earth.x + dis*Math.cos(dir));
			int tmpY = (int) (earth.y + dis*Math.sin(dir));
			AsteroidField aid = new AsteroidField(tmpX,tmpY);
			Fog.uncover(aid.x, aid.y, 300);
			new Wormhole(earth, aid);
			dis = Game.randomInt(400, 550);
			dir += (Math.PI/2.0f) + Game.randomFloat((float) (Math.PI));
			tmpX = (int) (earth.x + dis*Math.cos(dir));
			tmpY = (int) (earth.y + dis*Math.sin(dir));
			Nebula nid = new Nebula(tmpX,tmpY);
			Fog.uncover(nid.x, nid.y, 300);
			new Wormhole(earth, nid);
			
			do { // Randomly generate in game objects
				int randInt = Game.randomInt(totItems);
				if (randInt < numPlanet) {
					// Make a Planet
					new Planet(-1);
					numPlanet--;
				} else {
					randInt-=numPlanet;
					if (randInt < numAsteroidField) {
						// Make an AsteroidField
						new AsteroidField(-1,-1);
						numAsteroidField--;
					} else {
						randInt-=numAsteroidField;
						if (randInt < numNebula) {
							// Make a Nebula
							new Nebula(-1,-1);
							numNebula--;
						} else {
							// Make an Antimatter
							new Antimatter();
							numAntimatter--;
						}
					}
				}
				totItems--;
			} while (totItems != 0 && !roomCreationErrorFlag);
		} while (roomCreationErrorFlag);
		
		// Create all the necessary in game buttons
		muteBtn = new MuteBtn(ViewControl.getX()+Game.screenWidth-95, ViewControl.getY()+25, 16);
		pauseBtn = new PauseBtn(ViewControl.getX()+Game.screenWidth-60, ViewControl.getY()+25, 16);
		closeBtn = new CloseBtn(ViewControl.getX()+Game.screenWidth-25, ViewControl.getY()+25, 16);
		createWormholeBtn = new CreateWormholeBtn(Game.screenWidth - 350, Game.screenHeight - 25, 150, 40);
		createProbeBtn = new CreateProbeBtn(Game.screenWidth - 175, Game.screenHeight - 25, 150, 40);
		randomEventRefTime = System.nanoTime();
		secondRefTime = System.nanoTime();
		Sound.playMusic(Sound.MUSIC_DARKSPACE);
	}
	
	// Updates the locations of buttons to handle changing screen sizes
	public void update() {
		createWormholeBtn.updateLocation(ViewControl.getX() + Game.screenWidth - 350, ViewControl.getY() + Game.screenHeight - 25);
		createProbeBtn.updateLocation(ViewControl.getX() + Game.screenWidth - 175, ViewControl.getY() + Game.screenHeight - 25);
		muteBtn.updateLocation(ViewControl.getX()+Game.screenWidth-95, ViewControl.getY()+25);
		pauseBtn.updateLocation(ViewControl.getX()+Game.screenWidth-60, ViewControl.getY()+25);
		closeBtn.updateLocation(ViewControl.getX()+Game.screenWidth-25, ViewControl.getY()+25);
		randomEventCurrentTime = System.nanoTime();
		secondCurrentTime = System.nanoTime();
		long delTime = (secondCurrentTime - secondRefTime)/(1000000000);
		if(delTime >= 1){
			secondPassed = true;
			secondRefTime = secondCurrentTime = System.nanoTime();
		}
		// get the time difference from the reference to now and convert from nanoseconds to seconds
		delTime = (randomEventCurrentTime - randomEventRefTime)/(1000000000);
		if(delTime >= 30 && spareTime-- <= 0){
			spareTime = 0;
			new RandomEventGenerator();
			randomEventRefTime = randomEventCurrentTime = System.nanoTime();
		}
		Planet.calculateDisFromEarth();
		if (totalFuel <= 5.0f) {
			Sound.loopEffect(Sound.EFFECT_FUELALARM);
		} else {
			Sound.stopEffect(Sound.EFFECT_FUELALARM);
		}
	}
	
	// Draw in game screen
	public void draw() {
		Graphics2D gh = Game.getGraphicsHandle();
		frameCount++;
		gh.translate(ViewControl.getX(), ViewControl.getY());
		// Draw Top HUD
		gh.setColor(new Color(1.0f,1.0f,1.0f,0.60f));
		gh.fillRect(0, 0, Game.screenWidth, 50);
		
		// Draw number of planets remaining to be conquered
		String infoText = String.format("Planets Conquered: %d / %d", Planet.numConquered, Planet.numPlanets);
		gh.setFont(new Font("Serif", Font.PLAIN, 20));
		gh.setColor(Color.BLACK);
		gh.drawString(infoText,600, 30);
		
		// Draw Game Complete HUD
		if (gameComplete) {
			drawGameCompleteHud();
		} else if (gameFailed) {
			// Draw Rectangle
			gh.setColor(new Color(1.0f,1.0f,1.0f,0.80f));
			int width = 500;
			int height = 300;
			gh.fillRect(Game.screenWidth/2-width/2, Game.screenHeight/2-height/2, width, height);
			
			// Draw "PAUSED" Text
			gh.setFont(new Font("Serif", Font.PLAIN, 40));
			gh.setColor(Color.BLUE);
			String text = "You Lost!";
			FontRenderContext frc = gh.getFontRenderContext();
			Rectangle2D rect = gh.getFont().getStringBounds(text, frc);
			gh.drawChars(text.toCharArray(), 0, text.length(), (int)(Game.screenWidth/2 - rect.getWidth()/2), (int)(Game.screenHeight/2));
		} else if (Game.isPaused) {
			// Draw pause screen
			// Draw Rectangle
			gh.setColor(new Color(1.0f,1.0f,1.0f,0.80f));
			int width = 500;
			int height = 300;
			gh.fillRect(Game.screenWidth/2-width/2, Game.screenHeight/2-height/2, width, height);
			
			// Draw "PAUSED" Text
			gh.setFont(new Font("Serif", Font.PLAIN, 40));
			gh.setColor(Color.BLUE);
			String text = "PAUSED";
			FontRenderContext frc = gh.getFontRenderContext();
			Rectangle2D rect = gh.getFont().getStringBounds(text, frc);
			gh.drawChars(text.toCharArray(), 0, text.length(), (int)(Game.screenWidth/2 - rect.getWidth()/2), (int)(Game.screenHeight/2));
		}
		// Draw Bottom HUD
		gh.setColor(new Color(1.0f,1.0f,1.0f,0.60f));
		gh.fillRect(0, Game.screenHeight-50, Game.screenWidth, 50);
		if (selectedPlanet != null) { // Draw selected planet name on HUD
			String pName = "Name: " + selectedPlanet.getName();
			gh.setFont(new Font("Serif", Font.PLAIN, 20));
			gh.setColor(Color.BLACK);
			gh.drawChars(pName.toCharArray(), 0, pName.length(), 25, Game.screenHeight - 20);
			
			String amountOre = String.format("Ore: %.2f / %.2f", selectedPlanet.ore, selectedPlanet.maxOre);
			String amountFuel = String.format("Fuel: %.2f / %.2f", selectedPlanet.fuel, selectedPlanet.maxFuel);			
			gh.setFont(new Font("Serif", Font.PLAIN, 20));
			gh.setColor(Color.BLACK);
			gh.drawChars(amountOre.toCharArray(), 0, amountOre.length(), 200, Game.screenHeight - 20);
			gh.drawChars(amountFuel.toCharArray(), 0, amountFuel.length(), 400, Game.screenHeight - 20);
		}
		// Display ore and fuel remaining on top of screen
		String allOre = String.format("Total Ore: %.2f", totalOre);
		String allFuel = String.format("Total Fuel: %.2f", totalFuel);			
		gh.setFont(new Font("Serif", Font.PLAIN, 20));
		gh.setColor(Color.BLACK);
		gh.drawString(allOre,200, 30);
		// Flash fuel amount when low
		if ((totalFuel <= 5.0f && (frameCount/8)%2==0) || (totalFuel <= 10.0f && totalFuel > 5.0f && (frameCount/32)%2==0)) {
			gh.setColor(Color.RED);
		} else {
			gh.setColor(Color.BLACK);
		}
		gh.drawString(allFuel, 400, 30);
		drawTopDownMap();
		gh.translate(-ViewControl.getX(), -ViewControl.getY());
	}
	
	// Draw game complete window when user wins
	private void drawGameCompleteHud() {
		Graphics2D gh = Game.getGraphicsHandle();
		
		// Draw Rectangle
		gh.setColor(new Color(1.0f,1.0f,1.0f,0.80f));
		int width = 500;
		int height = 300;
		gh.fillRect(Game.screenWidth/2-width/2, Game.screenHeight/2-height/2, width, height);
		
		// Draw "You WON!" Text
		gh.setFont(new Font("Serif", Font.PLAIN, 40));
		gh.setColor(Color.BLUE);
		String text = "You WON!";
		FontRenderContext frc = gh.getFontRenderContext();
		Rectangle2D rect = gh.getFont().getStringBounds(text, frc);
		gh.drawChars(text.toCharArray(), 0, text.length(), (int)(Game.screenWidth/2 - rect.getWidth()/2), (int)(Game.screenHeight/2 - 100 + rect.getHeight()/2)-10);
		
		// Setup some stuff for text drawing
		gh.setFont(new Font("Serif", Font.PLAIN, 20));
		gh.setColor(Color.BLUE);
		
		// Draw Optimal Distance
		text = "Optimal Wormhole Distance: " + getOptimalDist();
		frc = gh.getFontRenderContext();
		rect = gh.getFont().getStringBounds(text, frc);
		gh.drawChars(text.toCharArray(), 0, text.length(), (int)(Game.screenWidth/2 - 225), (int)(Game.screenHeight/2 - 60 + rect.getHeight()/2)-10);
		
		// Draw Player's Wormhole Distance
		text = "Your Wormhole Distance: " + Wormhole.getTotalDist();
		frc = gh.getFontRenderContext();
		rect = gh.getFont().getStringBounds(text, frc);
		gh.drawChars(text.toCharArray(), 0, text.length(), (int)(Game.screenWidth/2 - 225), (int)(Game.screenHeight/2 - 30 + rect.getHeight()/2)-10);
		
		// Draw Score
		text = "Your Score: " + String.format("%.2f", (getOptimalDist() / Wormhole.getTotalDist()) * 100) + "%";
		frc = gh.getFontRenderContext();
		rect = gh.getFont().getStringBounds(text, frc);
		gh.drawChars(text.toCharArray(), 0, text.length(), (int)(Game.screenWidth/2 - 225), (int)(Game.screenHeight/2 - 0 + rect.getHeight()/2)-10);
	}
	
	// Draws the top down map in the top right corner of the screen
	private void drawTopDownMap() {
		Graphics2D gh = Game.getGraphicsHandle();
		int tdHeight = Game.screenHeight / 5;
		int tdWidth = (int) (tdHeight * ((float)width/(float)height));
		int tdOffsetX = Game.screenWidth - tdWidth - 20;
		int tdOffsetY = 20 + 50;
		float scaleFactor = (float)tdHeight/height;
		gh.setStroke(new BasicStroke(2f));
		gh.setColor(new Color(0.0f,0.0f,0.0f,0.85f));
		gh.fillRect(tdOffsetX, tdOffsetY, tdWidth, tdHeight);
		gh.translate(tdOffsetX, tdOffsetY);
		Wormhole.drawTdIcons(scaleFactor);
		if (minTree != null) {
			drawOptimalGraph(scaleFactor);
		}
		// Draw top-down icons for objects
		Planet.drawTdIcons(scaleFactor);
		Blackhole.drawTdIcons(scaleFactor);
		// Draw containing box
		gh.setStroke(new BasicStroke(2f));
		gh.setColor(Color.YELLOW);
		gh.drawRect((int)(ViewControl.getX()*scaleFactor), (int)(ViewControl.getY()*scaleFactor), 
				(int)(Game.screenWidth*scaleFactor), (int)(Game.screenHeight*scaleFactor));	
		gh.setColor(new Color(1.0f,1.0f,1.0f,1.0f));
		gh.drawRect(0, 0, tdWidth, tdHeight);
		gh.translate(-tdOffsetX, -tdOffsetY);
	}
	
	public void destroy() {
		earth = null;		
	}
	
	public static int getWidth() {
		return width;
	}
	public static int getHeight() {
		return height;
	}
	public static void setSelectedPlanet(Planet id) {
		selectedPlanet = id;
	}
	public static Planet getSelectedPlanet() {
		return selectedPlanet;
	}
	
	// Handle wormhole buttons
	public static void wormholePlaced() {
		createWormholeBtn.setSelected(false);
	}
	
	public static Planet getEarth() {return earth;}
	
	// Sets probe and wormhole buttons to be visible
	public static void setButtonsVisible(Boolean val) {
		createProbeBtn.setVisible(val);
		createWormholeBtn.setVisible(val);
	}
	
	// Returns whether or not the current game is winnable
	public static boolean isGameWinnable() {
		SimpleWeightedGraph<Planet, DefaultWeightedEdge> theGraph = Planet.getGraph(null);
		ConnectivityInspector conInspector = new ConnectivityInspector(theGraph);
		return conInspector.isGraphConnected();
	}
	
	// Runs at end of game - generates optimum route tree
	public static void winGame() {
		Sound.stopEffect(Sound.EFFECT_FUELALARM);
		Sound.stopMusic();
		Sound.effect(Sound.EFFECT_WIN);
		GameScreen.gameComplete = true;
		Game.isPaused = true;
		winPlanetGraph = Planet.getGraph(null);
		minTree = Planet.getMinTree(winPlanetGraph);
	}
	
	// Runs when user loses - plays sounds and sets stuff up
	public static void loseGame() {
		Sound.stopEffect(Sound.EFFECT_FUELALARM);
		totalFuel = 0.0f;
		Game.isPaused = true;
		Sound.stopMusic();
		Sound.effect(Sound.EFFECT_LOSE);
		gameFailed = true;
	}
	
	// Returns the distance for the optimal tree
	private static double getOptimalDist() {
		return minTree.getSpanningTreeCost();
	}
	
	// Draws the optimal graph in the top down view
	private static void drawOptimalGraph(float sf) {
		Graphics2D gh = Game.getGraphicsHandle();
		Set edgeSet = minTree.getEdgeSet();
		Iterator edgeSetIter = edgeSet.iterator();
		while (edgeSetIter.hasNext()) {
			DefaultWeightedEdge con = (DefaultWeightedEdge) edgeSetIter.next();
			Planet source = winPlanetGraph.getEdgeSource(con);
			Planet target = winPlanetGraph.getEdgeTarget(con);
			int x1 = (int) (source.x * sf);
			int y1 = (int) (source.y * sf);
			int x2 = (int) (target.x * sf);
			int y2 = (int) (target.y * sf);
			gh.setColor(Color.green);
			gh.drawLine(x1, y1, x2, y2);
		}
	}
	
	public static void throwRoomCreationError() {
		roomCreationErrorFlag = true;
	}
}